Classification & CV Example


Yeon Soo, Choi



Application with Default Data


ISLR 패키지의 Default 데이터를 이용해 전처리 및 EDA 과정은 생략하고 공부한 Classification 모델의 구현 그리고 Cross-Validation 의 과정까지 포함해 모델을 적합하여 성능을 비교하는 실습을 진행하였다.

library(ISLR)
library(knitr)
library(ggplot2)
library(caret)
## Loading required package: lattice
library(MASS)

Defaut 데이터는 incomebalance 그리고 종속변수로서 부도 여부에 대한 정보를 제공하는 default 변수를 포함하고 있다.

dim(Default)
## [1] 10000     4
kable(head(Default))
default student balance income
No No 729.5265 44361.625
No Yes 817.1804 12106.135
No No 1073.5492 31767.139
No No 529.2506 35704.494
No No 785.6559 38463.496
No Yes 919.5885 7491.559

종속변수가 'Yes' , 'No' 로 Binary Classification 문제이기 때문에 적절한 분류 모형의 선택과 복잡도를 Cross-Validation (10-Fold) 과정을 통해 보정과 함께 최적화하는 과정을 진행할 것이다.

독립변수인 incomebalance 를 모두 사용하여 default 에 대한 예측을 위한 분류 모형으로 공부한 Logistic Regression Model, LDA, QDA, KNN 들을 고려하였다.


Fitting Logistic Regerssion Model


10-Fold CV 로 모델의 성능을 추정할 것이므로 이를 진행하고 모델 적합을 실시한다.

set.seed(2013122044)
# define training control
train_control= trainControl(method="cv", number=10)

logistic=train(default~.,data=Default,trControl=train_control,method='glm')

logistic
## Generalized Linear Model 
## 
## 10000 samples
##     3 predictor
##     2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 9000, 8999, 9000, 9000, 9000, 9000, ... 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.9730992  0.4202143
summary(logistic)
## 
## Call:
## NULL
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.4691  -0.1418  -0.0557  -0.0203   3.7383  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -1.087e+01  4.923e-01 -22.080  < 2e-16 ***
## studentYes  -6.468e-01  2.363e-01  -2.738  0.00619 ** 
## balance      5.737e-03  2.319e-04  24.738  < 2e-16 ***
## income       3.033e-06  8.203e-06   0.370  0.71152    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 2920.6  on 9999  degrees of freedom
## Residual deviance: 1571.5  on 9996  degrees of freedom
## AIC: 1579.5
## 
## Number of Fisher Scoring iterations: 8

모형 적합 결과 income 변수가 유의하지 않다는 결과가 확인되어 이를 제하고 다시 모형을 적합하였다.

## logistic regression
logistic=train(default~student+balance, data=Default,trControl=train_control,method='glm')
logistic
## Generalized Linear Model 
## 
## 10000 samples
##     2 predictor
##     2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 9000, 9000, 8999, 9001, 9000, 9000, ... 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.9732002  0.4223812
summary(logistic)
## 
## Call:
## NULL
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.4578  -0.1422  -0.0559  -0.0203   3.7435  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -1.075e+01  3.692e-01 -29.116  < 2e-16 ***
## studentYes  -7.149e-01  1.475e-01  -4.846 1.26e-06 ***
## balance      5.738e-03  2.318e-04  24.750  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 2920.6  on 9999  degrees of freedom
## Residual deviance: 1571.7  on 9997  degrees of freedom
## AIC: 1577.7
## 
## Number of Fisher Scoring iterations: 8

적합 결과 분류 모델의 성능을 확인해보자.

## predictions
pred_logistic=predict(logistic,Default)
                      #type='prob'

## confusion matrix
kable(table(pred_logistic,Default$default))
No Yes
No 9628 228
Yes 39 105
## estimated test error rate from 10-fold CV
estimated_test_error_logistic=1-logistic$results[,2]

## ROC Curve
library(ROCR)
## Loading required package: gplots

## 
## Attaching package: 'gplots'

## The following object is masked from 'package:stats':
## 
##     lowess
prediction=prediction(predict(logistic,Default,type='prob')[,2],Default$default)


perf_AUC=performance(prediction,"auc") 
AUC_logistic=perf_AUC@y.values[[1]]

perf_ROC=performance(prediction,"tpr","fpr") 
plot(perf_ROC, main="ROC plot")
text(0.5,0.5,paste("AUC = ",format(AUC_logistic, digits=5, scientific=FALSE)))


Fitting LDA


## lda
lda=train(default~student+balance, data=Default,trControl=train_control,method='lda')
lda
## Linear Discriminant Analysis 
## 
## 10000 samples
##     2 predictor
##     2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 8999, 8999, 9000, 9000, 8999, 9001, ... 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.9725012  0.3481767

적합 결과 분류 모델의 성능을 확인해보자.

## predictions
pred_lda=predict(lda,Default)
                      #type='prob'

## confusion matrix
kable(table(pred_lda,Default$default))
No Yes
No 9644 252
Yes 23 81
## estimated test error rate from 10-fold CV
estimated_test_error_lda=1-lda$results[,2]

## ROC Curve
library(ROCR)
prediction=prediction(predict(lda,Default,type='prob')[,2],Default$default)


perf_AUC=performance(prediction,"auc") 
AUC_lda=perf_AUC@y.values[[1]]

perf_ROC=performance(prediction,"tpr","fpr") 
plot(perf_ROC, main="ROC plot")
text(0.5,0.5,paste("AUC = ",format(AUC_lda, digits=5, scientific=FALSE)))


Fitting QDA


실제 Decision Boundary가 선형이 아닐 경우, 즉 더 복잡한 모델의 경우인 QDA 적합을 시도하였다.

## lda
qda=train(default~student+balance, data=Default,trControl=train_control,method='qda')
qda
## Quadratic Discriminant Analysis 
## 
## 10000 samples
##     2 predictor
##     2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 9000, 8999, 9001, 9000, 9000, 9001, ... 
## Resampling results:
## 
##   Accuracy  Kappa    
##   0.972799  0.3785879

적합 결과 분류 모델의 성능을 확인해보자.

## predictions
pred_qda=predict(qda,Default)
                      #type='prob'

## confusion matrix
kable(table(pred_qda,Default$default))
No Yes
No 9637 244
Yes 30 89
## estimated test error rate from 10-fold CV
estimated_test_error_qda=1-qda$results[,2]

## ROC Curve
library(ROCR)
prediction=prediction(predict(qda,Default,type='prob')[,2],Default$default)


perf_AUC=performance(prediction,"auc") 
AUC_qda=perf_AUC@y.values[[1]]

perf_ROC=performance(prediction,"tpr","fpr") 
plot(perf_ROC, main="ROC plot")
text(0.5,0.5,paste("AUC = ",format(AUC_qda, digits=5, scientific=FALSE)))


Fitting KNN


k값을 1에서 100까지 조절해가며 KNN 적합 또한 시도하였다.

caret 패키지에서는 k-Fold Cross Validation 까지 진행하며 Accuracy가 가장 큰 K값 또한 찾아주기 때문에 이를 이용해 K=9 값을 이용하여 계산하였다.

## KNN 
knn=train(default~student+balance, data=Default,trControl=train_control,method='knn')
knn
## k-Nearest Neighbors 
## 
## 10000 samples
##     2 predictor
##     2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 9000, 9000, 9000, 9000, 9000, 9000, ... 
## Resampling results across tuning parameters:
## 
##   k  Accuracy   Kappa    
##   5  0.9683000  0.3893633
##   7  0.9707002  0.3999523
##   9  0.9717003  0.4158985
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 9.

적합 결과 분류 모델의 성능을 확인해보자.

## predictions
pred_knn=predict(knn,Default)
                      #type='prob'

## confusion matrix
kable(table(pred_knn,Default$default))
No Yes
No 9620 218
Yes 47 115
## estimated test error rate from 10-fold CV
estimated_test_error_knn=1-knn$results[3,2]

## ROC Curve
library(ROCR)
prediction=prediction(predict(knn,Default,type='prob')[,2],Default$default)

perf_AUC=performance(prediction,"auc") 
AUC_knn=perf_AUC@y.values[[1]]

perf_ROC=performance(prediction,"tpr","fpr") 
plot(perf_ROC, main="ROC plot")
text(0.5,0.5,paste("AUC = ",format(AUC_knn, digits=5, scientific=FALSE)))

지금까지 적합한 모델들의 성능을 비교해본 결과는 다음과 같다.

Accuracy (Estimated Test Error Rate)

error_rate_final=data.frame(Logistic=estimated_test_error_logistic,LDA=estimated_test_error_lda,QDA=estimated_test_error_qda,KNN=estimated_test_error_knn)

kable(error_rate_final)
Logistic LDA QDA KNN
0.0267998 0.0274988 0.027201 0.0282997

오차율의 측면에서는 CV 결과 로지스틱 회귀 모형의 성능이 가장 좋았다.

결국 실제 decision boundary 들이 선형으로 설명될 수 있는 간단한 모형이 적절하다는 것이다.

따라서 이 데이터를 가지고 복잡한 모형들을 적합시켰을 때 오히려 모형의 성능이 더 좋지 않았을 것이라고 짐작 가능하다.

하지만, Confusion Matrix 를 기반으로한 ROC Curve 그리고 AUC 를 통해서 본 결과는 조금 다르다.

Confusion Matrix, ROC, AUC

auc_final=data.frame(Logistic=AUC_logistic,LDA=AUC_lda,QDA=AUC_qda,KNN=AUC_knn)

kable(auc_final)
Logistic LDA QDA KNN
0.9495476 0.9495584 0.9495317 0.9747236

AUC 의 값은 KNN 의 값이 가장 높다. 하지만 이 또한 train 하에서 계산된 값이기 때문에 절대 정확하다고 할 수 없다.

전체적인 오차율보다는 공부한 민감도와 False Positive Rate 까지 고려했을 때 좋은 모형은 무엇일까라는 의문이 들어 이에 대해 쓰이는 지표 중에 하나인 F1 Score 를 찾을 수 있었다.

F_1=\frac{2}{\frac{1}{recall}+\frac{1}{precision}}=2\frac{precision\times{recall}}{predcision+recall}

따라서 이 지표에 대해 10-Fold CV 까지 진행한 결과 최적의 모델이 무엇인지 확인하기 위해 F1-Score 가 최대화되는 모델을 찾는 과정을 수행하였다.

library(MLmetrics)
## 
## Attaching package: 'MLmetrics'

## The following objects are masked from 'package:caret':
## 
##     MAE, RMSE

## The following object is masked from 'package:base':
## 
##     Recall
##f1 score
f1=function(data, lev = NULL, model = NULL) {
  f1_val=F1_Score(y_pred = data$pred, y_true = data$obs)
  c(F1 = f1_val)
}

##
train_control= trainControl(method="cv", number=10,summaryFunction=f1)

## logistic
logistic=train(default~student+balance, data=Default,trControl=train_control,method='glm',metric='F1')
f1_logistic=logistic$results[,2]
## lda
lda=train(default~student+balance, data=Default,trControl=train_control,method='lda',metric='F1')
f1_lda=lda$results[,2]
## qda
qda=train(default~student+balance, data=Default,trControl=train_control,method='qda',metric='F1')
f1_qda=qda$results[,2]
## knn
knn=train(default~student+balance, data=Default,trControl=train_control,method='knn',metric='F1')
f1_knn=knn$results[3,2]

f1_final=data.frame(Logistic=f1_logistic,LDA=f1_lda,QDA=f1_qda,KNN=f1_knn)

kable(f1_final)
Logistic LDA QDA KNN
0.9863772 0.9859941 0.9860356 0.9855377

최종적으로 F1-Score 의 경우에도 로지스틱 회귀 모형이 가장 성능이 좋다는 것을 확인할 수 있다.